package cn.zk.commom.tcp.obd;

import cn.zk.commom.tcp.obd.*;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
@Component
public class Position {
    public final boolean  mIsValid;             //位置信息是否有效
    public final boolean  mIsAddItemCarData;            //位置信息是否有效
    public final boolean  mIsAddItemTruckData;          //位置信息是否有效
    public final boolean  mIsAddItemNewEnergyData;      //位置信息是否有效
    public final boolean  mIsAddItemPeripheralData;     //位置信息是否有效
    public final boolean  mIsAddItemAlarmEventData;     //位置信息是否有效

    public final long     mWarnInfo;          //报警标志
    public final long     mStateInfo;         //车辆状态
    public final double   mLatitude;          //纬度，6个小数点
    public final double   mLongitude;         //经度，6个小数点
    public final int      mAltitude;          //海拔高度,单位为米(m)
    public final double   mSpeed;             //速度，1个小数点,1/10km/h
    public final double      mDirection;         //方向，0-359,正北为0,顺时针
    public final long   mTime;              //时间	BCD[6]	YY-MM-DD-hh-mm-ss(GMT+8 设备上报采用北京时间基准)

    public final PositionItemBasicData mAddItemBasicData;      //扩展数据项：基础数据流附表
    public final PositionItemCarData mAddItemCarData;        //扩展数据项：轿车扩展数据流
    public final PositionItemTruckData mAddItemTruckData;      //扩展数据项：货车数据项
    public final PositionItemNewEnergyData mAddItemNewEnergyData;  //扩展数据项：新能源汽车数据项
    public final PositionItemPeripheralData mAddItemPeripheralData;  //扩展数据项：扩展外设数据项附表
    public final PositionItemAlarmEventData     mAddItemAlarmEventData;  //扩展数据项：报警命令ID及描述附表


    //报警标志位定义
    public class WarnInfoBit {
        public final Boolean mEmergency;                      //bit[0],1: 紧急报瞥触动报警开关后触发,	收到应答后清零
        public final Boolean mOverSpeed;                      //bit[1],1：超速报警,	标志维持至报警条件解除
        public final Boolean mFatigueDriving;                 //bit[2],1：疲劳驾驶,	标志维持至报警条件解除
        public final Boolean mEarlyWarning;                   //bit[3],1：预警,	收到应答后清零
        public final Boolean mGNSSFailureAlarm;               //bit[4],1：GNSS模块发生故障,	标志维持至报警条件解除
        public final Boolean mGNSSAntennaNotConnected;        //bit[5],1：GNSS天线未接或被剪断,	标志维持至报警条件解除
        public final Boolean mGNSSAntennaShortCircuit;        //bit[6],1：GNSS天线短路,	标志维持至报警条件解除
        public final Boolean mMainPowerUnderVoltage;          //bit[7],1：终端主电源欠压,	标志维持至报警条件解除
        public final Boolean mMainPowerDown;                  //bit[8],1：终端主电源掉电,	标志维持至报警条件解除
        public final Boolean mLCDAlarm;                       //bit[9],1：终端LCD或显示器故障,	标志维持至报警条件解除
        public final Boolean mTTSAlarm;                       //bit[10],1：TTS模块故障,	标志维持至报警条件解除
        public final Boolean mCameraAlarm;                    //bit[11],1: 摄像头故障,	标志维持至报警条件解除
        public final Boolean mDrivingOvertime;                //bit[18],1: 当天累计驾驶超时,	标志维持至报警条件解除
        public final Boolean mParkingOvertime;                //bit[19],1：超时停车,	标志维持至报警条件解除
        public final Boolean mAreaInvalid;                    //bit[20],1：进出区域,	收到应答后清零
        public final Boolean mRoutesInvalid;                  //bit[21],1: 进出路线,	收到应答后清零
        public final Boolean mRoadTravelTimeInvalid;          //bit[22],1: 路段行驶时间不足/过长,	收到应答后清零
        public final Boolean mRouteDeviation;                 //bit[23],1: 路线偏离报警	标志维持至报警条件解除
        public final Boolean mVssAlarm;                       //bit[24],1：车辆VSS故障	标志维持至报警条件解除
        public final Boolean mOilQuantityAlarm;               //bit[25],1：车辆油量异常	标志维持至报警条件解除
        public final Boolean mVehicleStolen;                  //bit[26],1：车辆被盗(通过车辆防盗器)	标志维持至报警条件解除
        public final Boolean mIgnitionInvalid;                //bit[27],1：车辆非法点火	收到应答后清零
        public final Boolean mMoveInvalid;                    //bit[28],1：车辆非法位移	收到应答后清零
        public final long mValue;                        //十进制表示

        public WarnInfoBit() {
            this.mValue = mWarnInfo;
            this.mEmergency = (mWarnInfo & 0x1) == 1;
            this.mOverSpeed = ((mWarnInfo >> 1) & 0x1) == 1;
            this.mFatigueDriving = ((mWarnInfo >> 2) & 0x1) == 1;
            this.mEarlyWarning = ((mWarnInfo >> 3) & 0x1) == 1;
            this.mGNSSFailureAlarm = ((mWarnInfo>> 4) & 0x1) == 1;
            this.mGNSSAntennaNotConnected = ((mWarnInfo>> 5) & 0x1) == 1;
            this.mGNSSAntennaShortCircuit = ((mWarnInfo >> 6) & 0x1) == 1;
            this.mMainPowerUnderVoltage = ((mWarnInfo >> 7) & 0x1) == 1;
            this.mMainPowerDown = ((mWarnInfo >> 8) & 0x1) == 1;
            this.mLCDAlarm = ((mWarnInfo >> 9) & 0x1) == 1;
            this.mTTSAlarm = ((mWarnInfo >> 10) & 0x1) == 1;
            this.mCameraAlarm = ((mWarnInfo >> 11) & 0x1) == 1;
            this.mDrivingOvertime = ((mWarnInfo >> 18) & 0x1) == 1;
            this.mParkingOvertime = ((mWarnInfo >> 19) & 0x1) == 1;
            this.mAreaInvalid = ((mWarnInfo >> 20) & 0x1) == 1;
            this.mRoutesInvalid = ((mWarnInfo >> 21) & 0x1) == 1;
            this.mRoadTravelTimeInvalid = ((mWarnInfo >> 22) & 0x1) == 1;
            this.mRouteDeviation = ((mWarnInfo >> 23) & 0x1) == 1;
            this.mVssAlarm = ((mWarnInfo >> 24) & 0x1) == 1;
            this.mOilQuantityAlarm = ((mWarnInfo >> 25) & 0x1) == 1;
            this.mVehicleStolen = ((mWarnInfo >> 26) & 0x1) == 1;
            this.mIgnitionInvalid = ((mWarnInfo >> 27) & 0x1) == 1;
            this.mMoveInvalid = ((mWarnInfo >> 28) & 0x1) == 1;
        }
    }

    //状态标志位定义
    public class StateInfoBit {
        public final boolean mIsACC;                        //bit[0],0: ACC关;1:ACC开
        public final boolean mIsLocation;                   //bit[1],0:未定位;1:定位
        public final boolean mIsLinesOfLatitude;            //bit[2],0:北纬:1:南纬
        public final boolean mIsEastWestLongitude;          //bit[3],0:东经;1:西经
        public final boolean mIsStopStatus;                 //bit[4],0:运营状态:1:停运状态
        public final boolean mIsPositionEncryption;         //bit[5],0:经纬度未经保密插件加密;l:经纬度已经保密插件加密
        public final boolean mIsOilCircuitDisconnected;     //bit[10],0:车辆油路正常:1:车辆油路断开
        public final boolean mIsPowerDisconnected;          //bit[11],0:车辆电路正常:1:车辆电路断开
        public final boolean mIsDoorLocking;                //bit[12],0:车门解锁；1：车门加锁
        public final boolean mIsMaintenanceMode;            //bit[13],0:正常模式；  1:维修模式
        public final boolean mIsWifiOpen;                   //bit[14],0：WIFI关闭；1:WIFI开启
        public final boolean mIsTirePressureOk;             //bit[15],0：胎压433模块正常；1：胎压433模块异常
        public final boolean mIsBluetoothOk;                ////bit[16],0：蓝牙正常；1：蓝牙异常
        public final boolean mIsUpMsgNeedRespond;           //bit[17],0：上行数据不需要上位机应答，1：上行数据需要上位机应答
        public final long mValue;                           //十进制表示
        //
        public StateInfoBit() {
            this.mValue = mStateInfo;
            this.mIsACC = (mStateInfo & 0x1) == 1;
            this.mIsLocation = ((mStateInfo >> 1) & 0x1) == 1;
            this.mIsLinesOfLatitude = ((mStateInfo >> 2) & 0x1) == 1;
            this.mIsEastWestLongitude = ((mStateInfo >> 3) & 0x1) == 1;
            this.mIsStopStatus = ((mStateInfo >> 4) & 0x1) == 1;
            this.mIsPositionEncryption = ((mStateInfo >> 5) & 0x1) == 1;
            this.mIsOilCircuitDisconnected = ((mStateInfo >> 10) & 0x1) == 1;
            this.mIsPowerDisconnected = ((mStateInfo >> 11) & 0x1) == 1;
            this.mIsDoorLocking = ((mStateInfo >> 12) & 0x1) == 1;
            this.mIsMaintenanceMode = ((mStateInfo >> 13) & 0x1) == 1;
            this.mIsWifiOpen = ((mStateInfo >> 14) & 0x1) == 1;
            this.mIsTirePressureOk = ((mStateInfo >> 15) & 0x1) == 1;
            this.mIsBluetoothOk = ((mStateInfo >> 16) & 0x1) == 1;
            this.mIsUpMsgNeedRespond = ((mStateInfo >> 17) & 0x1) == 1;
        }
    }

    public Position(byte[] data) {
        ArrayList<byte[]> infos = SplitSinglePositionData(data);

        if(infos.size() == 0) {
            mIsValid = false;
            mIsAddItemCarData = false;
            mIsAddItemTruckData = false;
            mIsAddItemNewEnergyData = false;
            mIsAddItemPeripheralData = false;
            mIsAddItemAlarmEventData = false;

            mWarnInfo = 0;
            mStateInfo = 0;
            mLatitude = 0;
            mLongitude = 0;
            mAltitude = 0;
            mSpeed = 0;
            mDirection = 0;
            mTime = 0;

            mAddItemBasicData = null;
            mAddItemCarData = null;
            mAddItemTruckData = null;
            mAddItemNewEnergyData = null;
            mAddItemPeripheralData = null;
            mAddItemAlarmEventData = null;
            return;
        }

        //基础数据
        byte[] pos = infos.get(0);
        mIsValid = true;
        mWarnInfo = ByteArrayToLong(pos, 0, 4);
        mStateInfo = ByteArrayToLong(pos, 4, 4);
        mLatitude = ByteArrayToLong(pos, 8, 4) / 1000000.0;
        mLongitude = ByteArrayToLong(pos, 12, 4) / 1000000.0;
        mAltitude = (int) ByteArrayToLong(pos, 16, 2);
        mSpeed = (int) ByteArrayToLong(pos, 18, 2) / 10.0;
        mDirection = (int) ByteArrayToLong(pos, 20, 2);

        //时间，6个BCD码，实际为12个数字，年月日时分秒，年保留2位
        StringBuilder str1 = new StringBuilder();
        for (int i = 22; i < 28; ++i) {
            str1.append(String.format("%02x", pos[i]));
        }
        mTime =  new Long(str1.toString());


        //
        PositionItemBasicData      _AddItemBasicData = null;
        PositionItemCarData        _AddItemCarData = null;
        PositionItemTruckData      _AddItemTruckData = null;
        PositionItemNewEnergyData  _AddItemNewEnergyData = null;
        PositionItemPeripheralData _AddItemPeripheralData = null;
        PositionItemAlarmEventData _AddItemAlarmEventData = null;
        for(int i = 1; i < infos.size(); ++i)
        {
            byte[] var = infos.get(i);
            byte[] sub_data = new byte[var[1]];
            System.arraycopy(var, 2, sub_data, 0, sub_data.length);

            switch (var[0]) {
                case (byte)0xEA://基础数据项
                    _AddItemBasicData = new PositionItemBasicData(sub_data);
                    break;
                case (byte)0xEB://轿车数据项
                    _AddItemCarData = new PositionItemCarData(sub_data);
                    break;
                case (byte)0xEC://货车数据项
                    _AddItemTruckData = new PositionItemTruckData(sub_data);
                    break;
                case (byte)0xED://新能源车数据项
                    _AddItemNewEnergyData = new PositionItemNewEnergyData(sub_data);
                    break;
                case (byte)0xEE://外设数据项
                    _AddItemPeripheralData = new PositionItemPeripheralData(sub_data);
                    break;
                case (byte)0xFA://报警事件ID
                    _AddItemAlarmEventData = new PositionItemAlarmEventData(sub_data);
                    break;
                default: break;
            }
        }

        mIsAddItemCarData = (null != _AddItemCarData);
        mIsAddItemTruckData = (null != _AddItemTruckData);
        mIsAddItemNewEnergyData = (null != _AddItemNewEnergyData);
        mIsAddItemPeripheralData = (null != _AddItemPeripheralData);
        mIsAddItemAlarmEventData = (null != _AddItemAlarmEventData);

        mAddItemBasicData = _AddItemBasicData;
        mAddItemCarData = _AddItemCarData;
        mAddItemTruckData = _AddItemTruckData;
        mAddItemNewEnergyData = _AddItemNewEnergyData;
        mAddItemPeripheralData = _AddItemPeripheralData;
        mAddItemAlarmEventData = _AddItemAlarmEventData;
    }
    public static long ByteArrayToLong(byte[] data, int index ,int len) {
        if(index < 0 || len <= 0 || index >= data.length || len + index > data.length)
            return  0;

        //
        long ret = (data[index] & 0xff);
        for(int i = 1; i < len; ++i)
        {
            ret = (ret << 8)  + (data[i + index] & 0xff);
        }

        return  ret;
    }
    //位置信息由基础信息和扩展信息组成，
    //基础信息占28字节，剩下得是扩展信息
    private ArrayList<byte[]> SplitSinglePositionData(byte[] data) {
        ArrayList<byte[]> ret = new ArrayList<>();
        if(data.length < 28){
            return ret;
        }



        //拷贝基础位置信息
        byte[] _pos = new byte[28];
        System.arraycopy(data, 0, _pos, 0, 28);
        ret.add(_pos);

        //拆分扩展信息
        int sub_index = 28;
        while(true)
        {
            if(sub_index + 2 > data.length ){
                break;
            }
            int sub_len  = data[sub_index + 1];   //子类型长度
            int new_index = sub_index + sub_len + 2;

            if(new_index > data.length){
                break;
            }
            _pos = new byte[sub_len + 2];
            System.arraycopy(data, sub_index, _pos, 0, _pos.length);
            ret.add(_pos);

            //
            sub_index = new_index;
        }

        return  ret;
    }

}

